﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.Composition.Hosting;
using Microsoft.ComponentModel.Composition.Extensions.Web;
using System.ComponentModel.Composition;

namespace Steve.UI
{
    public static class HttpContextBaseExtensions
    {
        const string UrlMatchKey = "UrlMatch";

        public static UrlObject GetUrlMatch(this HttpContextBase ctx)
        {
            if (ctx == null) throw new ArgumentNullException("ctx");

            return (UrlObject)ctx.Items[UrlMatchKey];
        }

        public static void SetUrlMatch(this HttpContextBase ctx, UrlObject value)
        {
            if (ctx == null) throw new ArgumentNullException("ctx");

            ctx.Items[UrlMatchKey] = value;
        }
    }
}

namespace Steve.UI.Modules
{
    public class SteveSiteModule : IHttpModule
    {
#if TRACE
        [Import]
        public ILoggingService Log { get; set; }
#endif
        [Import]
        public RewriterBase Rewriter { get; set; }
        [Import]
        public SiteExplorerRepository Data { get; set; }
        [Import]
        public IHttpContextBaseBuilder ContextBuilder { get; set; }
        [ImportMany(typeof(ISiteModuleExtension))]
        public IEnumerable<ISiteModuleExtension> Extensions { get; set; }

        public void Dispose()
        {
        }

        void IHttpModule.Init(HttpApplication context)
        {
            Init(context);
        }

        protected virtual void Init(HttpApplication context)
        {
            if (context == null) throw new ArgumentNullException("context");

            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.PostAuthenticateRequest += new EventHandler(context_PostAuthenticateRequest);

            MefHttpApplication.ScopeManager.Compose(this);
        }

        void context_PostAuthenticateRequest(object sender, EventArgs e)
        {
            HandlePostAuthenticateRequest(ContextBuilder.BuildCurrent());
        }

        //internal for unit testing
        internal void HandlePostAuthenticateRequest(HttpContextBase ctx)
        {
            RunExtensions(e => e.ProcessPostAuthenticateRequest(ctx), "PostAuthenticateRequest extensions");
        }

        //internal for unit testing
        [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "BeginRequest")]
        internal void HandleBeginRequest(HttpContextBase ctx)
        {
#if TRACE
            Log.Info(TraceCategory.SiteModule, "BeginRequest");
#endif

            //look up rewrite
            var lazy = Data.NavigateTo(ctx.Request.Url);
            UrlObject urlMatch = null;
            if(lazy!=null) urlMatch = lazy.Value;
            ctx.SetUrlMatch(urlMatch);

            RunExtensions(e => e.ProcessBeginRequest(ctx, urlMatch), "BeginRequest extensions");

            //look up rewrite
            var result = Rewriter.Rewrite(ctx.Request.Url, urlMatch);

            if (result != null && result.ToUrl != null)
            {
                //TODO: handle internal rewrites instead of just 301's
                ctx.Response.StatusCode = 301;
                ctx.Response.Status = "301 Moved Permanently";
                ctx.Response.RedirectLocation = result.ToUrl.ToString();

                RunExtensions(e => e.ProcessRewrite(ctx, result), "Rewrite extensions");

                ctx.Response.End();
            }
        }

        private void RunExtensions(Action<ISiteModuleExtension> action, string messageSuffix = "extensions")
        {
            if (string.IsNullOrEmpty(messageSuffix)) messageSuffix = "extensions";

#if TRACE
            Log.Info(TraceCategory.SiteModule, "Begin " + messageSuffix);
#endif

            if (Extensions == null) return;

            foreach (var e in Extensions)
            {
                if(e != null) action(e);
            }

#if TRACE
            Log.Info(TraceCategory.SiteModule, "End " + messageSuffix);
#endif
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            HandleBeginRequest(ContextBuilder.BuildCurrent());
        }
    }
}
